﻿-------------------------------------------------------------------------------
-- Календарь. © SimSU
-------------------------------------------------------------------------------

---- Настройки
local function Settings()
-- Начало файла Profile\SimSU\Common_Calendar.cfg
return{
  Key="CtrlShiftF12"; --Prior=50;
}
-- Конец файла Profile\SimSU\Common_Calendar.cfg
end

---- Локализация
_G.far.lang=far.lang or win.GetEnv("farlang")
-- Встроенные языки / Buildin laguages
local function Messages()
if far.lang=="Russian" then
-- Начало файла Profile\SimSU\Common_CalendarRussian.lng
return{
  Descr="Календарь. © SimSU";
  Title="Календарь";
  DaysOfWeek={"ПН","ВТ","СР","ЧТ","ПТ","СБ","ВС"};
  Months={"Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"};
  Ins="Вставить";
  Ok="Закрыть";
}
-- Конец файла Profile\SimSU\Common_CalendarRussian.lng
else--if far.lang=="English" then
-- Begin of file Profile\SimSU\Common_CalendarEnglish.lng
return{
  Descr="Calendar. © SimSU";
  Title="Calendar";
  DaysOfWeek={"MO","TU","WE","TH","FR","SA","SU"};
  Months={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
  Ins="Insert";
  Ok="Close";
}
-- End of file Profile\SimSU\Common_CalendarEnglish.lng
end end

local M=(loadfile(win.GetEnv("FARPROFILE").."\\SimSU\\Common_Calendar"..far.lang..".lng") or Messages)()
local S=(loadfile(win.GetEnv("FARLOCALPROFILE").."\\SimSU\\Common_Calendar.cfg") or loadfile(win.GetEnv("FARPROFILE").."\\SimSU\\Common_Calendar.cfg") or Settings)()

local SimSU=SimSU or {}
SimSU.Common_Calendar={}
-------------------------------------------------------------------------------
local F=far.Flags
local MSinDay=1000*60*60*24
local DaysInMonths={[0]=31,31,28,31,30,31,30,31,31,30,31,30,31,[13]=31}
local Text

function SimSU.Common_Calendar.DateTime()
  return win.FileTimeToSystemTime(win.FileTimeToLocalFileTime(win.GetSystemTimeAsFileTime()))
--[[ Возвращает таблицу с полями:
  wYear:          number
  wMonth:         number
  wDayOfWeek:     number
  wDay:           number
  wHour:          number
  wMinute:        number
  wSecond:        number
  wMilliseconds:  number
]]
end

local Curent=SimSU.Common_Calendar.DateTime()

function SimSU.Common_Calendar.Leap(DateTime) -- високосный год?
  return math.fmod(DateTime.wYear,100)==0 and math.fmod(DateTime.wYear,400)==0 and true or math.fmod(DateTime.wYear,100)~=0 and math.fmod(DateTime.wYear,4)==0 and true or false
end

function SimSU.Common_Calendar.Incday(DateTime,Days)
  local dt=win.FileTimeToSystemTime(win.SystemTimeToFileTime(DateTime)+Days*MSinDay) or DateTime
  DaysInMonths[2]=SimSU.Common_Calendar.Leap(dt) and 29 or 28
  return dt
end
--==
  SimSU.Common_Calendar.Incday(Curent,0) -- Чтобы февраль откорректировать.
--==
function SimSU.Common_Calendar.Monday(DateTime)
  return SimSU.Common_Calendar.Incday(DateTime,1-(DateTime.wDayOfWeek==0 and 7 or DateTime.wDayOfWeek) or 0)
end

function SimSU.Common_Calendar.Firstday(DateTime)
  return SimSU.Common_Calendar.Incday(DateTime,1-DateTime.wDay)
end

function SimSU.Common_Calendar.Lastday(DateTime)
  return SimSU.Common_Calendar.Incday(DaysInMonths[DateTime.wMonth]-DateTime.wDay)
end

function SimSU.Common_Calendar.IncMonth(DateTime)
  return SimSU.Common_Calendar.Incday(DateTime, DateTime.wDay>DaysInMonths[DateTime.wMonth+1] and DaysInMonths[DateTime.wMonth+1] or DaysInMonths[DateTime.wMonth])
end

function SimSU.Common_Calendar.DecMonth(DateTime)
  return SimSU.Common_Calendar.Incday(DateTime,DateTime.wDay>DaysInMonths[DateTime.wMonth-1] and -DateTime.wDay or -DaysInMonths[DateTime.wMonth-1])
end

function SimSU.Common_Calendar.IncYear(DateTime)
  return SimSU.Common_Calendar.Incday(DateTime,
    DateTime.wMonth>2 and SimSU.Common_Calendar.Leap(SimSU.Common_Calendar.Incday(DateTime,365)) and 366 or
    DateTime.wMonth==2 and DateTime.wDay==29 and 365 or
    DateTime.wMonth<3 and SimSU.Common_Calendar.Leap(DateTime) and 366 or 365
  )
end

function SimSU.Common_Calendar.DecYear(DateTime)
  return SimSU.Common_Calendar.Incday(DateTime,
    DateTime.wMonth>2 and SimSU.Common_Calendar.Leap(DateTime) and -366 or
    DateTime.wMonth==2 and DateTime.wDay==29 and -366 or
    DateTime.wMonth<3 and SimSU.Common_Calendar.Leap(SimSU.Common_Calendar.Incday(DateTime,-365)) and -366 or -365
  )
end

function SimSU.Common_Calendar.Calendar(DateTime)
  local dt=DateTime and win.SystemTimeToFileTime(DateTime) and DateTime or Curent
  local ITic
  local Items = {}
  Items[01]={F.DI_DOUBLEBOX,3, 1,32,18,0,0,0,0,M.Title}
  Items[02]={F.DI_TEXT,    -1, 2, 0, 2,0,0,0,F.DIF_SEPARATOR,""}
  Items[03]={F.DI_BUTTON,   4, 3, 0, 3,0,0,0,F.DIF_BTNNOCLOSE,"<"} --Год назад
  Items[04]={F.DI_FIXEDIT, 16, 3,19, 3,0,0,"9999",F.DIF_MASKEDIT,""} --Год
  Items[05]={F.DI_BUTTON,  27, 3, 0, 3,0,0,0,F.DIF_BTNNOCLOSE,">"} --Год вперёд
  Items[06]={F.DI_TEXT,    -1, 4, 0, 4,0,0,0,F.DIF_SEPARATOR,""}
  local Months={}
  for m=1,12 do
    Months[m]={}; Months[m]["Text"]=M.Months[m]
  end
  Items[07]={F.DI_BUTTON,   4, 5, 0,21,0,0,0,F.DIF_BTNNOCLOSE,"<"} --Месяц назад
  Items[08]={F.DI_COMBOBOX,10, 5,24, 5,Months,0,0,F.DIF_DROPDOWNLIST,""} --Месяц
  Items[09]={F.DI_BUTTON,  27, 5, 0,21,0,0,0,F.DIF_BTNNOCLOSE,">"} --Месяц вперёд
  Items[10]={F.DI_TEXT,     0, 6, 0, 6,0,0,0,F.DIF_SEPARATOR,""}
  Items[11]={F.DI_TEXT,     0,14, 0,14,0,0,0,F.DIF_SEPARATOR,""}
  local yd=6
  for d=1,7 do
    Items[#Items+1]={F.DI_TEXT, 5, yd+d, 0, yd+d,0,0,0,0,M.DaysOfWeek[d]}
  end
  local IF=18
  for w=0,5 do
    for d=1,7 do
      Items[#Items+1]={F.DI_TEXT, 8+w*4, yd+d, 0, yd+d,0,0,0,0,""}
    end
  end
  Items[61]={F.DI_USERCONTROL,8,7,31, 13,0,0,0,0} --Движение по дням
  Items[62]={F.DI_FIXEDIT,  7,15,16,15,0,0,"99.99.9999",F.DIF_MASKEDIT+F.DIF_READONLY,""}
  Items[63]={F.DI_BUTTON,   18,15, 0,15,0,0,0,0,M.Ins} -- Вставить дату
  Items[64]={F.DI_TEXT,     0,16, 0,16,0,0,0,F.DIF_SEPARATOR,""}
  Items[65]={F.DI_BUTTON,   0,17, 0,17,0,0,0,F.DIF_DEFAULTBUTTON+F.DIF_CENTERGROUP+F.DIF_FOCUS,M.Ok}

  local function Rebuild(hDlg,dt)
    far.SendDlgMessage(hDlg,"DM_ENABLEREDRAW",0)
    far.SendDlgMessage(hDlg,"DM_SETTEXT",04,tostring(dt.wYear))
    far.SendDlgMessage(hDlg,"DM_LISTSETCURPOS",08,{SelectPos=dt.wMonth})
    local day=SimSU.Common_Calendar.Monday(SimSU.Common_Calendar.Firstday(dt))
    ITic=nil
    for w=0,5 do
      for d=1,7 do
        far.SendDlgMessage(hDlg,"DM_SETTEXT",IF+w*7+d,("%3s "):format(day.wDay))
        far.SendDlgMessage(hDlg,"DM_ENABLE",IF+w*7+d,day.wMonth==dt.wMonth and 1 or 0)
        if day.wYear==Curent.wYear and day.wMonth==Curent.wMonth and day.wDay==Curent.wDay then
          far.SendDlgMessage(hDlg,"DM_SETTEXT",IF+w*7+d,("[%2s]"):format(day.wDay))
          ITic= ITic or w*7+d
        elseif day.wDay==dt.wDay then
          far.SendDlgMessage(hDlg,"DM_SETTEXT",IF+w*7+d,("{%2s}"):format(day.wDay))
          ITic=day.wMonth==dt.wMonth and w*7+d or ITic
        else
          far.SendDlgMessage(hDlg,"DM_SETTEXT",IF+w*7+d,("%3s "):format(day.wDay))
        end
        day=SimSU.Common_Calendar.Incday(day,1)
      end
    end
    far.SendDlgMessage(hDlg,"DM_SETTEXT",62,string.format("%02d.%02d.%4d",dt.wDay,dt.wMonth,dt.wYear))
    far.SendDlgMessage(hDlg,"DM_ENABLEREDRAW",1)
  end

  local function DlgProc(hDlg,Msg,Param1,Param2)
    if Msg==F.DN_INITDIALOG then
        Rebuild(hDlg,dt)
    elseif Msg==F.DN_BTNCLICK and Param1==63 then
      Text=far.SendDlgMessage(hDlg, "DM_GETTEXT", 62, nil)
    elseif Msg==F.DN_BTNCLICK then
      if Param1==03 then --Год назад
        dt=SimSU.Common_Calendar.DecYear(dt)
      elseif Param1==05 then --Год вперёд
        dt=SimSU.Common_Calendar.IncYear(dt)
      elseif Param1==07 then  --Месяц назад
        dt=SimSU.Common_Calendar.DecMonth(dt)
      elseif Param1==09 then --Месяц вперёд
        dt=SimSU.Common_Calendar.IncMonth(dt)
      end
      Rebuild(hDlg,dt)
    elseif Msg==F.DN_EDITCHANGE and Param1==04 then
      local oldY=dt.wYear
      dt.wYear=tonumber(far.SendDlgMessage(hDlg, "DM_GETTEXT", 04, nil))
      if win.SystemTimeToFileTime(dt) then
        Rebuild(hDlg,dt)
      else
        dt.wYear=oldY
      end
    elseif Msg==F.DN_EDITCHANGE and Param1==08 then
      local oldM=dt.wMonth
      dt.wMonth=(far.SendDlgMessage(hDlg, "DM_LISTGETCURPOS", 08, nil)).SelectPos
      if win.SystemTimeToFileTime(dt) then
      else
        far.SendDlgMessage(hDlg,"DM_LISTSETCURPOS",08,{SelectPos=dt.wMonth})
        dt.wMonth=oldM
      end
      Rebuild(hDlg,dt)
    elseif Msg==F.DN_CONTROLINPUT and Param1==61 then
      if band(Param2.ControlKeyState,0x0008+0x0004)~=0 then
        if Param2.VirtualKeyCode==37 then --Влево
          dt=SimSU.Common_Calendar.DecMonth(dt)
        elseif Param2.VirtualKeyCode==38 then --Вверх
          dt=SimSU.Common_Calendar.DecYear(dt,-1)
        elseif Param2.VirtualKeyCode==39 then --Вправо
          dt=SimSU.Common_Calendar.IncMonth(dt,7)
        elseif Param2.VirtualKeyCode==40 then --Вниз
          dt=SimSU.Common_Calendar.IncYear(dt,1)
        end
      elseif Param2.VirtualKeyCode==37 then
        dt=SimSU.Common_Calendar.Incday(dt,-7)
      elseif Param2.VirtualKeyCode==38 then
        dt=SimSU.Common_Calendar.Incday(dt,-1)
      elseif Param2.VirtualKeyCode==39 then
        dt=SimSU.Common_Calendar.Incday(dt,7)
      elseif Param2.VirtualKeyCode==40 then
        dt=SimSU.Common_Calendar.Incday(dt,1)
      end
      if Param2.ButtonState==1 then
        local i=math.floor(Param2.MousePositionX/4)*7+Param2.MousePositionY+1
        dt=SimSU.Common_Calendar.Incday(dt,i-ITic)
      end
      Rebuild(hDlg,dt)
    end
  end

  local guid = win.Uuid("615d826b-3921-48bb-9cf2-c6d345833855")
  far.Dialog (guid,-1,-1,36,20,nil,Items,nil,DlgProc)
  if Text then print(Text) end
end
-------------------------------------------------------------------------------
if not Macro then return {Common_Calendar=SimSU.Common_Calendar} end

local ok, mod = pcall(require,"SimSU.Common_Calendar"); if ok then SimSU=mod else _G.SimSU=SimSU end
-------------------------------------------------------------------------------

Macro {area="Common"; key=S.Key; priority=S.Prior; description=M.Descr; flags="";
  condition=function() return true end;
  action=SimSU.Common_Calendar.Calendar;
}
